﻿; (function (global) {

    'use strict';

    global.HintsProvider = function HintsProvider(solver) {
        this.solver = solver;
    }

    HintsProvider.prototype.getHint = function (grid) {
        var workingGrid = ObjectSerializer.duplicate(grid);
        var isAKnownState = this.isAKnownState(grid);
        if (!isAKnownState) {
            SolvingHelper.fillCandidates(workingGrid);
        }

        var gridBeforeSolve = ObjectSerializer.duplicate(workingGrid);
        var solveResult = this.solver.stepSolve(workingGrid);

        if (solveResult != null) {
            if (solveResult.strategy.type == 'elimination' && !isAKnownState) {
                SolvingHelper.fillCandidates(grid);
            }

            if (solveResult.strategy instanceof Strategies.NakedSingle) {
                return this.getHintForNakedSingle(grid, gridBeforeSolve, solveResult);
            }

            if (solveResult.strategy instanceof Strategies.HiddenSingle) {
                return this.getHintForHiddenSingle(grid, gridBeforeSolve, solveResult);
            }

            var missingCandidates = GridHelper.getMissingCandidatesByCell(grid, workingGrid);
            if (solveResult.strategy instanceof Strategies.PointingPair) {
                return this.getHintForPointingPair(grid, gridBeforeSolve, missingCandidates, solveResult);
            }

            if (solveResult.strategy instanceof Strategies.PointingInside) {
                return this.getHintForPointingInside(grid, gridBeforeSolve, missingCandidates, solveResult);
            }

            if (solveResult.strategy instanceof Strategies.NakedPair) {
                return this.getHintForNakedPair(grid, gridBeforeSolve, missingCandidates, solveResult);
            }

            if (solveResult.strategy instanceof Strategies.Burma) {
                return this.getHintForBurma(grid, gridBeforeSolve, missingCandidates, solveResult);
            }

            if (solveResult.strategy instanceof Strategies.SimpleColoring) {
                return this.getHintForSimpleColoring(grid, gridBeforeSolve, missingCandidates, solveResult);
            }
        }
    }

    HintsProvider.prototype.isAKnownState = function (grid) {
        var currenState = ObjectSerializer.serialize(grid);
        var workingGrid = ObjectSerializer.duplicate(grid);
        SolvingHelper.fillCandidates(workingGrid);
        var workingState = ObjectSerializer.serialize(workingGrid);

        var eliminationStrategy = true;
        while (workingState != currenState && eliminationStrategy) {
            var solveResult = this.solver.stepSolve(workingGrid);
            if (!solveResult)
                return false;
            eliminationStrategy = solveResult.strategy.type == "elimination";
            workingState = ObjectSerializer.serialize(workingGrid);
        }

        return workingState == currenState;
    };

    HintsProvider.prototype.getHintForNakedSingle = function (grid, gridBeforeSolve, solveResult) {
        var cell = solveResult.result;
        var originalCell = grid.getCell(cell.indices);
        var beforeSolveCell = gridBeforeSolve.getCell(cell.indices);
        originalCell.copy(beforeSolveCell);
        return { cell: originalCell, candidate: cell.value, strategy: solveResult.strategy };
    };

    HintsProvider.prototype.getHintForHiddenSingle = function (grid, gridBeforeSolve, solveResult) {
        var HiddenSingleResult = solveResult.result;
        var cell = HiddenSingleResult.cell;
        var relatedCells = null;
        if (HiddenSingleResult.deduction == "row") {
            relatedCells = grid.cells[cell.indices.i];
        } else if (HiddenSingleResult.deduction == "column") {
            relatedCells = grid.cellsByColumn[cell.indices.j];
        }
        else if (HiddenSingleResult.deduction == "region") {
            var regionId = RegionHelper.computedRegions[cell.indices.i][cell.indices.j];
            relatedCells = grid.cellsByRegion[regionId];
        }

        for (var i = 0; i < relatedCells.length; i++) {
            var origCell = relatedCells[i];
            var beforeSolveCell = gridBeforeSolve.getCell(origCell.indices);
            origCell.copy(beforeSolveCell);
        }

        var originalCell = grid.getCell(cell.indices);
        return { cell: originalCell, candidate: cell.value, relatedCells: relatedCells, strategy: solveResult.strategy };
    };

    HintsProvider.prototype.getHintForPointingPair = function (grid, gridBeforeSolve, missingCandidatesByCell, solveResult) {
        var pointingPairResult = solveResult.result;
        var sourceRegionId = pointingPairResult.sourceRegionId;
        var relatedCells = pointingPairResult.targetCells.concat(grid.cellsByRegion[sourceRegionId]).distinct();
        return {
            candidate: pointingPairResult.candidate,
            containingCells: pointingPairResult.containingCells,
            relatedCells: relatedCells,
            missingCandidatesCellsIndices: missingCandidatesByCell.map(function (o) { return o.cell.indices; }),
            strategy: solveResult.strategy
        };
    };

    HintsProvider.prototype.getHintForPointingInside = function (grid, gridBeforeSolve, missingCandidatesByCell, solveResult) {
        var pointingInsideResult = solveResult.result;
        var relatedCells = pointingInsideResult.targetCells.concat(pointingInsideResult.lineCells).distinct();
        return {
            candidate: pointingInsideResult.candidate,
            containingCells: pointingInsideResult.containingCells,
            relatedCells: relatedCells,
            missingCandidatesCellsIndices: missingCandidatesByCell.map(function (o) { return o.cell.indices; }),
            strategy: solveResult.strategy
        };
    };

    HintsProvider.prototype.getHintForNakedPair = function (grid, gridBeforeSolve, missingCandidatesByCell, solveResult) {
        var nakedPairResult = solveResult.result;
        return {
            candidates: nakedPairResult.candidates,
            containingCells: nakedPairResult.containingCells,
            relatedCells: nakedPairResult.targetCells,
            missingCandidatesCellsIndices: missingCandidatesByCell.map(function (o) { return o.cell.indices; }),
            strategy: solveResult.strategy
        };
    };

    HintsProvider.prototype.getHintForBurma = function (grid, gridBeforeSolve, missingCandidatesByCell, solveResult) {
        var burmaResult = solveResult.result;
        return {
            candidate: burmaResult.candidate,
            burmaSize: burmaResult.burmaSize,
            containingCells: burmaResult.containingCells,
            relatedCells: burmaResult.targetCells,
            missingCandidatesCellsIndices: missingCandidatesByCell.map(function (o) { return o.cell.indices; }),
            strategy: solveResult.strategy
        };
    };

    HintsProvider.prototype.getHintForSimpleColoring = function (grid, gridBeforeSolve, missingCandidatesByCell, solveResult) {
        var simpleColoringResult = solveResult.result;
        var chainsConflict = simpleColoringResult.conflicts.map(function (o) { return o.chain; });
        var chainsSeeTwo = simpleColoringResult.seeTwoColors.map(function (o) { return o.chain; });
        var chains = chainsConflict.concat(chainsSeeTwo).distinct();
        var chainsCellsArrs = chains.map(function (o) { return o.off.concat(o.on); });
        var chainsCells = [].concat.apply([], chainsCellsArrs);

        var cellsConflict = [];
        for (var i = 0; i < simpleColoringResult.conflicts.length; i++) {
            cellsConflict.pushArray(simpleColoringResult.conflicts[i].conflictingCells);
        }
        cellsConflict = cellsConflict.distinct();

        var cellsSeeTwo = simpleColoringResult.seeTwoColors.map(function (obj) { return obj.cell; });
        var relatedCells = chainsCells.concat(cellsConflict).concat(cellsSeeTwo).distinct();
        return {
            candidate: simpleColoringResult.candidate,
            chains: chains,
            cellsConflict: cellsConflict,
            cellsSeeTwo: cellsSeeTwo,
            relatedCells: relatedCells,
            strategy: solveResult.strategy
        };
    };

    HintsProvider.prototype.applyHint = function (grid) {
        var workingGrid = ObjectSerializer.duplicate(grid);
        var isAKnownState = this.isAKnownState(grid);
        if (!isAKnownState) {
            SolvingHelper.fillCandidates(workingGrid);
        }
        var solveResult = this.solver.stepSolve(workingGrid);

        if (solveResult != null) {

            if (solveResult.strategy instanceof Strategies.NakedSingle) {
                var cell = solveResult.result;
                var originalCell = grid.getCell(cell.indices);
                originalCell.setValue(cell.value);
                SolvingHelper.removeDependingCandidates(originalCell, grid);
            }
            else if (solveResult.strategy instanceof Strategies.HiddenSingle) {
                var HiddenSingleResult = solveResult.result;
                var cell = HiddenSingleResult.cell;
                var originalCell = grid.getCell(cell.indices);
                originalCell.setValue(cell.value);
                SolvingHelper.removeDependingCandidates(originalCell, grid);
            }
            else if (solveResult.strategy instanceof Strategies.PointingPair) {
                grid.copy(workingGrid);
            }
            else if (solveResult.strategy instanceof Strategies.PointingInside) {
                grid.copy(workingGrid);
            }
            else if (solveResult.strategy instanceof Strategies.NakedPair) {
                grid.copy(workingGrid);
            }
            else if (solveResult.strategy instanceof Strategies.Burma) {
                grid.copy(workingGrid);
            }
            else if (solveResult.strategy instanceof Strategies.SimpleColoring) {
                grid.copy(workingGrid);
            }
        }
    }
}(this));
